/*
 *  \file Cache.h
 *  gre
 *
 *  Created by Kutlak Roman on 20/10/2011.
 *  Copyright 2011 Kutlak Roman. All rights reserved.
 *
 */

#ifndef Cache_h 
#define Cache_h

// SYSTEM INCLUDES 
#include <map>
#include <deque>
#include <iosfwd>
#include <string>

#include "Utilities.h"

namespace kutlak {
namespace roman {
namespace gre {
    
    // some necessary forward declarations
    template <class K, class V>
    class Cache;
    
    template <class K, class V>
    class FileCache;
    
    template <class K, class V>
    std::istream& operator>>(std::istream& input, Cache<K,V>& obj);
    
    template <class K, class V>            
    std::ostream& operator<<(std::ostream& out, Cache<K,V>& obj);
    
    template <class K, class V>
    void swap(Cache<K,V>& left, Cache<K,V>& right);
    
    template <class K, class V>
    void swap(FileCache<K,V>& left, FileCache<K,V>& right);

    /*!
     * \brief Class that serves as a cache. 
     * 
     * The cache can be limited to a particular number of entries.
     * When new entry is inserted and the limit exceded, one of the less
     * used value is discarded.
     * On search, found value is promoted higher so that it is less likely
     * to get dropped on insert.
     */
    template < class K, class V > 
    class Cache { 
    public: 
#pragma mark -
#pragma mark lifecycle
        
        Cache(size_t maxSize = -1, const V& defaultValue = V());
        
        Cache(const Cache<K,V>& from);
        
        Cache(Cache<K,V>&& from);
        
        Cache<K, V>& operator=(Cache<K,V> from);
        
        virtual ~Cache(void) noexcept(true);
        
        friend void swap <>(Cache<K, V>& left, Cache<K, V>& right);
        
#pragma mark -
#pragma mark I/O
        
        friend std::istream& operator>> <>(std::istream& input, Cache<K,V>& obj);
        
        friend std::ostream& operator<< <>(std::ostream& out, Cache<K,V>& obj);
        
#pragma mark -
#pragma mark operators
        
        // add the content of rhs to this cache
        Cache<K,V>& operator+=(const Cache<K,V>& rhs);
        
        bool operator==(const Cache<K,V>& rhs) const;
        
        bool operator!=(const Cache<K,V>& rhs) const;
        
#pragma mark -
#pragma mark operations
        
        // returns the default value if the key does not exist
        V Find(const K& key) const;
        
        // returns true if key exists and sets result to the matching value
        bool Find(const K& key, V& result) const;
        
        bool Insert(const K& key, const V& value);
        
        bool Insert(K&& key, V&& value);
        
        void Clear();
        
#pragma mark -
#pragma mark access
        
        const V& DefaultValue() const;
        
        size_t Size() const;
        
        size_t MaxSize() const;
        
#pragma mark -
#pragma mark inquiry
        bool IsModified() const;
        
    protected:
        void Serialise(std::ostream& output) const;
        
        void Deserialise(std::istream& input);
        
    protected:
        bool fModified;
        size_t mMaxSize;
        V mDefaultValue;
        mutable std::map<K,V> mCache;
        mutable std::deque<K> mOrder;
    };
    
    /*! \brief Cache that can be saved into a file.
     *  It can maintain a file with cached results.
     *  TODO: implement some mechanism for allowing updating obsolete info
     *  NOTE: If using strings as keys and values, no spaces are allowed as 
     *  the cache uses operator >> to read the data. If needed, wrap the string
     *  in a class that overloads operator>> and uses getline() instead of >>.
     */
    template < typename K, typename V>
    class FileCache : public Cache<K,V> 
    {
    public:
        FileCache(const std::string& file = "",
                  size_t maxSize = -1,
                  const V& defaultValue = V());
        
        FileCache(const FileCache<K,V>& from);
        
        FileCache(FileCache<K,V>&& from);
        
        FileCache<K, V>& operator=(FileCache<K,V> from);
        
        virtual ~FileCache(void) noexcept(true);
        
        friend void swap <>(FileCache<K, V>& left, FileCache<K, V>& right);
        
#pragma mark -
#pragma mark operations
        
        bool operator==(const FileCache<K,V>& rhs) const;
        
        bool operator!=(const FileCache<K,V>& rhs) const;
        
        bool Save();
        
#pragma mark -
#pragma mark access
        const std::string& File() const;
        
        bool SetFile(const std::string& file, bool replace = true);
        
        bool WriteFile(const std::string& file) const;
        
        bool ReadFile(const std::string& file);
        
    protected:
        std::string mFileName; 
    };
    
    template< typename K, typename V>
    struct PairPred
    {
        PairPred(const K& key) : key_(key) {}
        
        bool operator()(const std::pair<K,V>& item) const
        { return item.first == key_; }
    
        K key_;
    };
    
} // end namespace gre
}
}

#endif // Cache_h


#include "Cache.cpp"
